//
// Created by myrica on 2/26/25.
//

#include "ASTVisitor/MLIRASTVisitor.h"
#include <iostream>
#include <ranges>
#include <mlir/Transforms/Passes.h>

#include "cpp-subprocess/cpp-subprocess/subprocess.hpp"
#include "llvm/AsmParser/Parser.h"
#include "llvm/Support/Casting.h"
#include "misc/tools.h"
#include "mlir/Conversion/ConvertToLLVM/ToLLVMPass.h"
#include "mlir/Target/LLVMIR/Export.h"
#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"

using llvm::isa;
using llvm::cast;
using llvm::cast_or_null;
using llvm::dyn_cast;

using std::literals::string_literals::operator ""s;
namespace sp = subprocess;

void ast_visitor::MLIR_ASTVisitor::visit(std::vector<ASTBase *> *decls) {
    pre_define();
    for (auto decl: *decls) {
        visitASTBase(decl);
    }

    if (emit_mlir)
        module.print(llvm::outs());

    mlir::PassManager mlir_pm(ctx);

    mlir_pm.addPass(irispass::createRemoveUnreachableOperationPass());
    mlir_pm.addNestedPass<mlir::func::FuncOp>(irispass::createMoveAllocaToEntryPass());
    mlir_pm.addPass(mlir::createCSEPass());
    mlir_pm.addPass(mlir::createMem2Reg());
    if (failed(mlir_pm.run(module))) {
        report_fatal_error("mlir optimization failed");
    }

    std::string buffer;
    raw_string_ostream ss(buffer);
    module.print(ss);
    auto mlir_opt = sp::Popen({
                                  "/home/myrica/tools/LLVM-20.1.0-Linux-X64/bin/mlir-opt",
                                  "--convert-linalg-to-affine-loops", "--expand-strided-metadata",
                                  "--lower-affine", "--convert-scf-to-cf", "--convert-cf-to-llvm=index-bitwidth=32",
                                  "--convert-arith-to-llvm=index-bitwidth=32",
                                  "--convert-func-to-llvm=index-bitwidth=32",
                                  "--convert-func-to-llvm=use-bare-ptr-memref-call-conv",
                                  "--finalize-memref-to-llvm=index-bitwidth=32", "--canonicalize",
                                  "--convert-to-llvm"
                              }, sp::input{sp::PIPE},
                              sp::output{sp::PIPE});
    mlir_opt.send(ss.str().data(), ss.str().size());
    auto mlir_opt_res = mlir_opt.communicate().first;
    auto mlir_translate = sp::Popen({"/home/myrica/tools/LLVM-20.1.0-Linux-X64/bin/mlir-translate", "--mlir-to-llvmir"},
                                    sp::input{sp::PIPE}, sp::output{sp::PIPE});
    mlir_translate.send(mlir_opt_res.buf.data(), mlir_opt_res.buf.size());
    auto res = mlir_translate.communicate().first;

    auto MemBuf = MemoryBuffer::getMemBufferCopy(res.buf.data());
    SMDiagnostic Err;
    M = parseAssembly(*MemBuf, Err, *llvm_context).release();

    PassBuilder PB;
    FunctionAnalysisManager FAM;
    PB.registerFunctionAnalyses(FAM);
    FunctionPassManager FPM;
    FPM.addPass(MoveAllocaToEntryPass());
    for (Function &F: *M) {
        if (!F.isDeclaration())
            FPM.run(F, FAM);
    }
    if (!M) {
        llvm_unreachable("Failed to emit LLVM IR");
    }

    if (emit_llvm_dialect)
        module.print(llvm::outs());
}

void ast_visitor::MLIR_ASTVisitor::pre_define() {
#define GEN_PREDEFINE_FUNC(_name, _ret_type, ...) \
    auto _name##_type =  builder->getFunctionType({__VA_ARGS__}, _ret_type); \
    auto _name##_fn = builder->create<mlir::func::FuncOp>(builder->getUnknownLoc(), #_name, _name##_type); \
    _name##_fn.setPrivate(); \
    scope.set(#_name, {_name##_fn, _name##_type});


    GEN_PREDEFINE_FUNC(putint, {}, i32);
    GEN_PREDEFINE_FUNC(putfloat, {}, f32);
    GEN_PREDEFINE_FUNC(putch, {}, i32);
    GEN_PREDEFINE_FUNC(getint, {i32});
    GEN_PREDEFINE_FUNC(getfloat, {f32});
    GEN_PREDEFINE_FUNC(getch, {i32});
    GEN_PREDEFINE_FUNC(putarray, {}, i32, ptr);
    GEN_PREDEFINE_FUNC(putfarray, {}, i32, ptr);
    GEN_PREDEFINE_FUNC(getarray, {i32}, ptr);
    GEN_PREDEFINE_FUNC(getfarray, {i32}, ptr);

#undef GEN_PREDEFINE_FUNC
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitDeclarationFunction(DeclarationFunction *f) {
    vector<mlir::Type> return_type; {
        const auto &ast_return_type = f->return_type;
        if (std::holds_alternative<type::Int *>(ast_return_type)) {
            return_type.push_back(i32);
        } else if (std::holds_alternative<type::Float *>(ast_return_type)) {
            return_type.push_back(f32);
        }
    }

    vector<mlir::Type> param_type;

    if (!f->params.empty()) {
        for (auto param: f->params) {
            mlir::Type base_type;
            if (std::holds_alternative<type::Int *>(param->type))
                base_type = i32;
            else if (std::holds_alternative<type::Float *>(param->type))
                base_type = f32;
            else
                llvm_unreachable("other types except i32 and f32");

            if (param->dimensions.empty())
                param_type.push_back(base_type);
            else {
                auto dims = param->dimensions | std::views::transform([this](Expression *exp) {
                    if (exp) {
                        auto res = visitExpression(exp);
                        assert(isa<mlir::arith::ConstantOp>(res.getDefiningOp()));
                        return static_cast<int64_t>(extractFromConstantValue<int>(res));
                    }
                    return mlir::ShapedType::kDynamic;
                }) | std::ranges::to<vector<int64_t> >();
                mlir::MemRefType memref_type;
                // 带有下标的函数参数一定是类似 int a[]类型的
                // 传递实际参数时，int a[10] -> f(a)
                // int a[5][10] -> f(a[5])
                // 都应该直接使用memref.cast而非subview
                if (dims.size() == 1) {
                    auto strides = vector<int64_t>(dims.size(), 1);
                    auto layout = mlir::StridedLayoutAttr::get(
                        ctx, dims[0] == mlir::ShapedType::kDynamic ? mlir::ShapedType::kDynamic : 0, strides);
                    memref_type = mlir::MemRefType::get(dims, base_type, layout);
                } else {
                    memref_type = mlir::MemRefType::get(dims, base_type);
                }
                param_type.push_back(memref_type);
            }
        }
    }


    auto func_type = builder->getFunctionType(param_type, return_type);
    auto fn = builder->create<mlir::func::FuncOp>(builder->getUnknownLoc(), f->name, func_type);
    scope.set(f->name, {fn, func_type});

    auto entry = fn.addEntryBlock();
    auto args = entry->getArguments();

    builder->setInsertionPointToEnd(entry);


    scope.enterScope();

    curr_func = fn;

    for (size_t i = 0; i < f->params.size(); ++i) {
        auto param_alloca = visitDeclarationBuiltinType(f->params[i]);
        builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), fn.getArgument(i), param_alloca);
    }

    if (!return_type.empty()) {
        curr_func_ret_value = builder->create<mlir::memref::AllocaOp>(builder->getUnknownLoc(),
                                                                      mlir::MemRefType::get({}, return_type[0]));
    }

    auto func_body = curr_func.addBlock();
    curr_func_ret_block = curr_func.addBlock();
    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), func_body);
    builder->setInsertionPointToStart(func_body);

    visitStatementCompound(f->content);

    if (builder->getBlock()->empty() || !builder->getBlock()->back().hasTrait<mlir::OpTrait::IsTerminator>())
        builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), curr_func_ret_block);
    builder->setInsertionPointToEnd(curr_func_ret_block);
    auto ret_value_range = curr_func_ret_value
                               ? mlir::ValueRange{
                                   builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), curr_func_ret_value)
                               }
                               : mlir::ValueRange{};
    builder->create<mlir::func::ReturnOp>(builder->getUnknownLoc(), ret_value_range);

    curr_func = nullptr;
    curr_func_ret_value = nullptr;
    curr_func_ret_block = nullptr;

    scope.leaveScope();


    builder->setInsertionPointToEnd(module.getBody());

    return nullptr;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpression(Expression *exp) {
    if (auto primary = dyn_cast<ExpressionPrimary>(exp)) {
        return visitExpressionPrimary(primary);
    }
    if (auto unary = dyn_cast<ExpressionUnary>(exp)) {
        return visitExpressionUnary(unary);
    }
    if (auto cast = dyn_cast<ExpressionCast>(exp)) {
        return visitExpressionCast(cast);
    }
    if (auto multi = dyn_cast<ExpressionMultiplicative>(exp)) {
        return visitExpressionMultiplicative(multi);
    }
    if (auto additive = dyn_cast<ExpressionAdditive>(exp)) {
        return visitExpressionAdditive(additive);
    }
    if (auto shift = dyn_cast<ExpressionShift>(exp)) {
        return visitExpressionShift(shift);
    }
    if (auto relational = dyn_cast<ExpressionRelational>(exp)) {
        return visitExpressionRelational(relational);
    }
    if (auto equality = dyn_cast<ExpressionEquality>(exp)) {
        return visitExpressionEquality(equality);
    }
    if (auto and_exp = dyn_cast<ExpressionAnd>(exp)) {
        return visitExpressionAnd(and_exp);
    }
    if (auto xor_exp = dyn_cast<ExpressionExclusiveOr>(exp)) {
        return visitExpressionExclusiveOr(xor_exp);
    }
    if (auto or_exp = dyn_cast<ExpressionInclusiveOr>(exp)) {
        return visitExpressionInclusiveOr(or_exp);
    }
    if (auto land = dyn_cast<ExpressionLogicalAnd>(exp)) {
        return visitExpressionLogicalAnd(land);
    }
    if (auto lor = dyn_cast<ExpressionLogicalOr>(exp)) {
        return visitExpressionLogicalOr(lor);
    }
    if (auto conditional = dyn_cast<ExpressionConditional>(exp)) {
        return visitExpressionConditional(conditional);
    }
    if (auto assign = dyn_cast<ExpressionAssignment>(exp)) {
        return visitExpressionAssignment(assign);
    }
    if (auto init = dyn_cast<ExpressionInitializerList>(exp)) {
        llvm_unreachable("visit initializer list shouldn't be called by visitExpression");
        // return visitExpressionInitializerList(init);
    }
    if (auto postfix = dyn_cast<ExpressionPostfix>(exp))
        return visitExpressionPostfix(postfix);
    llvm_unreachable("no more exp types");
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionPrimary(ExpressionPrimary *exp) {
    const auto &val = exp->value;

    if (std::holds_alternative<Expression *>(val)) {
        return visitExpression(std::get<Expression *>(val));
    }
    if (std::holds_alternative<int>(val)) {
        return builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                        builder->getIntegerAttr(
                                                            i32,
                                                            std::get<int>(val)));
    }
    if (std::holds_alternative<float>(val)) {
        return builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                        builder->getFloatAttr(
                                                            f32,
                                                            std::get<float>(val)));
    }

    const auto &name = std::get<std::string>(val);
    auto [value, type, global_attr] = scope.get(name);

    if (!value) {
        // global
        if (global_attr) {
            if (auto int_attr = dyn_cast<mlir::IntegerAttr>(global_attr)) {
                assert(int_attr.getType().isInteger(32));
                return builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(),
                                                                   static_cast<int32_t>(int_attr.getInt()),
                                                                   32);
            } else {
                auto float_attr = dyn_cast<mlir::FloatAttr>(global_attr);
                assert(float_attr && float_attr.getType().isF32());
                return getConstant(static_cast<float>(float_attr.getValueAsDouble()));
            }
        } else
            return builder->create<mlir::memref::GetGlobalOp>(builder->getUnknownLoc(), type, name);
    }
    // assert(value.getDefiningOp() && (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(value.getDefiningOp())));
    return value;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionUnary(ExpressionUnary *exp) {
    auto value = visitExpression(exp->exp);

    if (exp->ops.empty())
        return value;

    if (dyn_cast<mlir::arith::ConstantOp>(value.getDefiningOp())) {
        for (auto &op: std::ranges::reverse_view(exp->ops)) {
            switch (op) {
                case ASTOperator::AddressOf:
                    llvm_unreachable("unimplemented: address of in unary exps");
                    break;
                case ASTOperator::Deref:
                    llvm_unreachable("unimplemented: deref in unary exps");
                    break;
                case ASTOperator::Positive:
                    // do nothing
                    break;
                case ASTOperator::Negative:
                    if (isa<mlir::IntegerType>(value.getType())) {
                        value = getConstant(-extractFromConstantValue<int>(value));
                    } else {
                        value = getConstant(-extractFromConstantValue<float>(value));
                    }
                    break;
                case ASTOperator::BitNot:
                    if (isa<mlir::IntegerType>(value.getType())) {
                        value = getConstant(~extractFromConstantValue<int>(value));
                    } else {
                        llvm_unreachable("unimplemented: bitwise not for float");
                    }
                    break;
                case ASTOperator::Not:
                    if (isa<mlir::IntegerType>(value.getType())) {
                        value = getConstant(!extractFromConstantValue<int>(value));
                    } else {
                        value = getConstant(!extractFromConstantValue<float>(value));
                    }
                    assert(value.getType().isInteger(32));
                // value = builder->create<mlir::arith::ExtUIOp>(builder->getUnknownLoc(), i32, value);
                    break;
                case ASTOperator::PlusPlus:
                    llvm_unreachable("unimplemented: ++ prefix");
                    break;
                case ASTOperator::MinusMinus:
                    llvm_unreachable("unimplemented: -- prefix");
                    break;
                case ASTOperator::Sizeof:
                    llvm_unreachable("unimplemented: sizeof prefix");
                    break;
                default:
                    llvm_unreachable("not a unary op");
            }
        }
        return value;
    }

    if (dyn_cast<mlir::memref::AllocaOp>(value.getDefiningOp()))
        value = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), value, mlir::ValueRange{});


    for (auto &op: std::ranges::reverse_view(exp->ops)) {
        switch (op) {
            case ASTOperator::AddressOf:
                llvm_unreachable("unimplemented: address of in unary exps");
                break;
            case ASTOperator::Deref:
                llvm_unreachable("unimplemented: deref in unary exps");
                break;
            case ASTOperator::Positive:
                // do nothing
                break;
            case ASTOperator::Negative:
                if (isa<mlir::IntegerType>(value.getType())) {
                    value = builder->create<mlir::arith::SubIOp>(
                        builder->getUnknownLoc(),
                        builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                                 builder->getIntegerAttr(i32, 0)),
                        value
                    );
                } else {
                    value = builder->create<mlir::arith::NegFOp>(
                        builder->getUnknownLoc(),
                        value
                    );
                }
                break;
            case ASTOperator::BitNot:
                if (isa<mlir::IntegerType>(value.getType())) {
                    value = builder->create<mlir::arith::XOrIOp>(
                        builder->getUnknownLoc(),
                        builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                                 builder->getIntegerAttr(i32, -1)),
                        value
                    );
                } else {
                    llvm_unreachable("unimplemented: bitwise not for float");
                }
                break;
            case ASTOperator::Not:
                if (isa<mlir::IntegerType>(value.getType())) {
                    value = builder->create<mlir::arith::CmpIOp>(
                        builder->getUnknownLoc(),
                        mlir::arith::CmpIPredicate::eq,
                        builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                                 builder->getIntegerAttr(i32, 0)),
                        value
                    );
                } else {
                    value = builder->create<mlir::arith::CmpFOp>(
                        builder->getUnknownLoc(),
                        mlir::arith::CmpFPredicate::OEQ,
                        builder->create<mlir::arith::ConstantOp>(builder->getUnknownLoc(),
                                                                 builder->getFloatAttr(f32, 0)),
                        value
                    );
                }
                value = builder->create<mlir::arith::ExtUIOp>(builder->getUnknownLoc(), i32, value);
                break;
            case ASTOperator::PlusPlus:
                llvm_unreachable("unimplemented: ++ prefix");
                break;
            case ASTOperator::MinusMinus:
                llvm_unreachable("unimplemented: -- prefix");
                break;
            case ASTOperator::Sizeof:
                llvm_unreachable("unimplemented: sizeof prefix");
                break;
            default:
                llvm_unreachable("not a unary op");
        }
    }

    return value;
}

// 构造cast exp的时候unary分支提前返回
// 因此获取的ExpressionCast一定是包含了类型转换的结果
// 无须提前返回
mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionCast(ExpressionCast *exp) {
    auto raw = visitExpression(exp->exp);

    if (isa<mlir::arith::ConstantOp>(raw.getDefiningOp())) {
        if (isa<mlir::IntegerType>(raw.getType()) && std::holds_alternative<type::Float *>(exp->type)) {
            return getConstant(static_cast<float>(extractFromConstantValue<int>(raw)));
        }

        if (isa<mlir::FloatType>(raw.getType()) && std::holds_alternative<type::Int *>(exp->type)) {
            return getConstant(static_cast<int>(extractFromConstantValue<float>(raw)));
        }
    }

    // only int and float supported
    assert(std::holds_alternative<type::Int *>(exp->type) || std::holds_alternative<type::Float *>(exp->type));

    if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(raw.getDefiningOp()))
        raw = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), raw, mlir::ValueRange{});


    if (isa<mlir::IntegerType>(raw.getType()) && std::holds_alternative<type::Float *>(exp->type)) {
        raw = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, raw);
    }

    if (isa<mlir::FloatType>(raw.getType()) && std::holds_alternative<type::Int *>(exp->type)) {
        raw = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), i32, raw);
    }

    return raw;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionMultiplicative(ExpressionMultiplicative *exp) {
    auto base = visitExpression(exp->base);

    if (!base ||
        exp->others.empty() && isa<mlir::MemRefType>(base.getType()) && !dyn_cast<mlir::MemRefType>(base.getType()).
        getShape().empty())
        return base;

    if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(base.getDefiningOp())) {
        base = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), base, mlir::ValueRange{});
    }

    bool is_const = isa<mlir::arith::ConstantOp>(base.getDefiningOp());
    bool is_float = isa<mlir::Float32Type>(base.getType());
    int const_int = 114;
    float const_float = 514;
    if (is_const) {
        if (is_float)
            const_float = extractFromConstantValue<float>(base);
        else
            const_int = extractFromConstantValue<int>(base);
    }

    for (const auto &[op, nxt]: exp->others) {
        auto rhs = visitExpression(nxt);
        if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(rhs.getDefiningOp())) {
            rhs = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), rhs, mlir::ValueRange{});
        }
        // 多余的转换语句在后续lower到llvm dialect时可以消除掉
        assert(isa<mlir::FloatType>(base.getType()) || isa<mlir::IntegerType>(base.getType()));
        assert(isa<mlir::FloatType>(rhs.getType()) || isa<mlir::IntegerType>(rhs.getType()));

        is_const = is_const && isa<mlir::arith::ConstantOp>(rhs.getDefiningOp());

        if (is_const) {
            int rhs_const_int = 114;
            float rhs_const_float = 514;

            if (isa<mlir::FloatType>(rhs.getType()))
                rhs_const_float = extractFromConstantValue<float>(rhs);
            else
                rhs_const_int = extractFromConstantValue<int>(rhs);


            bool is_float_updated = is_float || isa<mlir::FloatType>(rhs.getType());
            if (is_float_updated) {
                if (!is_float)
                    const_float = static_cast<float>(const_int);
                if (!isa<mlir::FloatType>(rhs.getType()))
                    rhs_const_float = static_cast<float>(rhs_const_int);
            }

            is_float = is_float_updated;

            switch (op) {
                case ASTOperator::Times:
                    if (is_float)
                        const_float *= rhs_const_float;
                    else
                        const_int *= rhs_const_int;
                    break;
                case ASTOperator::Div:
                    if (is_float)
                        const_float /= rhs_const_float;
                    else
                        const_int /= rhs_const_int;
                    break;
                case ASTOperator::Mod:
                    if (is_float)
                        llvm_unreachable("mod is not for floats");
                    else
                        const_int %= rhs_const_int;
                    break;
                default:
                    llvm_unreachable("no other multiplicative op");
            }

            if (is_float)
                base = getConstant(const_float);
            else
                base = getConstant(const_int);

            continue;
        }

        is_float = isa<mlir::FloatType>(base.getType()) || isa<mlir::FloatType>(rhs.getType());

        if (is_float) {
            if (!isa<mlir::FloatType>(base.getType()))
                base = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, base);
            if (!isa<mlir::FloatType>(rhs.getType()))
                rhs = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, rhs);
        }
        switch (op) {
            case ASTOperator::Times:
                if (is_float)
                    base = builder->create<mlir::arith::MulFOp>(builder->getUnknownLoc(), base, rhs);
                else
                    base = builder->create<mlir::arith::MulIOp>(builder->getUnknownLoc(), base, rhs);
                break;
            case ASTOperator::Div:
                if (is_float)
                    base = builder->create<mlir::arith::DivFOp>(builder->getUnknownLoc(), base, rhs);
                else
                    base = builder->create<mlir::arith::DivSIOp>(builder->getUnknownLoc(), base, rhs);
                break;
            case ASTOperator::Mod:
                if (is_float)
                    base = builder->create<mlir::arith::RemFOp>(builder->getUnknownLoc(), base, rhs);
                else
                    base = builder->create<mlir::arith::RemSIOp>(builder->getUnknownLoc(), base, rhs);
                break;
            default:
                llvm_unreachable("no other multiplicative op");
        }
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionAdditive(ExpressionAdditive *exp) {
    auto base = visitExpression(exp->base);

    if (!base ||
        exp->others.empty() && isa<mlir::MemRefType>(base.getType()) && !dyn_cast<mlir::MemRefType>(base.getType()).
        getShape().empty())
        return base;

    if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(base.getDefiningOp())) {
        base = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), base, mlir::ValueRange{});
    }

    bool is_const = isa<mlir::arith::ConstantOp>(base.getDefiningOp());
    bool is_float = isa<mlir::Float32Type>(base.getType());
    int const_int = 114;
    float const_float = 514;
    if (is_const) {
        if (is_float)
            const_float = extractFromConstantValue<float>(base);
        else
            const_int = extractFromConstantValue<int>(base);
    }

    for (const auto &[op, nxt]: exp->others) {
        auto rhs = visitExpression(nxt);
        if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(rhs.getDefiningOp())) {
            rhs = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), rhs, mlir::ValueRange{});
        }
        // 多余的转换语句在后续lower到llvm dialect时可以消除掉
        assert(isa<mlir::FloatType>(base.getType()) || isa<mlir::IntegerType>(base.getType()));
        assert(isa<mlir::FloatType>(rhs.getType()) || isa<mlir::IntegerType>(rhs.getType()));

        is_const = is_const && isa<mlir::arith::ConstantOp>(rhs.getDefiningOp());

        if (is_const) {
            int rhs_const_int = 114;
            float rhs_const_float = 514;

            if (isa<mlir::FloatType>(rhs.getType()))
                rhs_const_float = extractFromConstantValue<float>(rhs);
            else
                rhs_const_int = extractFromConstantValue<int>(rhs);


            bool is_float_updated = is_float || isa<mlir::FloatType>(rhs.getType());
            if (is_float_updated) {
                if (!is_float)
                    const_float = static_cast<float>(const_int);
                if (!isa<mlir::FloatType>(rhs.getType()))
                    rhs_const_float = static_cast<float>(rhs_const_int);
            }

            is_float = is_float_updated;

            switch (op) {
                case ASTOperator::Add:
                    if (is_float)
                        const_float += rhs_const_float;
                    else
                        const_int += rhs_const_int;
                    break;
                case ASTOperator::Sub:
                    if (is_float)
                        const_float -= rhs_const_float;
                    else
                        const_int -= rhs_const_int;
                    break;
                default:
                    llvm_unreachable("no other additive op");
            }

            if (is_float)
                base = getConstant(const_float);
            else
                base = getConstant(const_int);

            continue;
        }

        is_float = isa<mlir::FloatType>(base.getType()) || isa<mlir::FloatType>(rhs.getType());

        if (is_float) {
            if (!isa<mlir::FloatType>(base.getType()))
                base = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, base);
            if (!isa<mlir::FloatType>(rhs.getType()))
                rhs = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, rhs);
        }
        switch (op) {
            case ASTOperator::Add:
                if (is_float)
                    base = builder->create<mlir::arith::AddFOp>(builder->getUnknownLoc(), base, rhs);
                else
                    base = builder->create<mlir::arith::AddIOp>(builder->getUnknownLoc(), base, rhs);
                break;
            case ASTOperator::Sub:
                if (is_float)
                    base = builder->create<mlir::arith::SubFOp>(builder->getUnknownLoc(), base, rhs);
                else
                    base = builder->create<mlir::arith::SubIOp>(builder->getUnknownLoc(), base, rhs);
                break;
            default:
                llvm_unreachable("no other additive op");
        }
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionShift(ExpressionShift *exp) {
    auto base = visitExpression(exp->base);
    if (exp->others.empty())
        return base;

    if (isa<mlir::memref::AllocaOp, mlir::memref::GetGlobalOp>(base.getDefiningOp())) {
        base = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), base, mlir::ValueRange{});
    }

    if (!isa<mlir::IntegerType>(base.getType()))
        llvm_unreachable("lhs of shift op can only be int");

    bool is_const = isa<mlir::arith::ConstantOp>(base.getDefiningOp());
    int const_int = 114;
    if (is_const)
        const_int = extractFromConstantValue<int>(base);

    for (const auto &[op, nxt]: exp->others) {
        auto rhs = visitExpression(nxt);
        if (!isa<mlir::IntegerType>(rhs.getType()))
            llvm_unreachable("rhs of shift op can only be int");

        is_const = is_const && isa<mlir::arith::ConstantOp>(rhs.getDefiningOp());

        if (is_const) {
            int rhs_const_int = extractFromConstantValue<int>(rhs);
            if (op == ASTOperator::LeftShift)
                const_int <<= rhs_const_int;
            else
                const_int >>= rhs_const_int;
            base = getConstant(const_int);
            continue;
        }

        if (op == ASTOperator::LeftShift)
            base = builder->create<mlir::arith::ShLIOp>(builder->getUnknownLoc(), base, rhs);
        else
            base = builder->create<mlir::arith::ShRSIOp>(builder->getUnknownLoc(), base, rhs);
    }
    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionRelational(ExpressionRelational *exp) {
    auto base = visitExpression(exp->base);
    if (exp->others.empty())
        return base;

    assert(isa<mlir::IntegerType>(base.getType()) || isa<mlir::FloatType>(base.getType()));

    for (const auto &[op, nxt]: exp->others) {
        auto rhs = visitExpression(nxt);
        assert(isa<mlir::IntegerType>(rhs.getType()) || isa<mlir::FloatType>(rhs.getType()));

        bool is_float = isa<mlir::FloatType>(base.getType()) || isa<mlir::FloatType>(rhs.getType());

        if (is_float) {
            if (!isa<mlir::FloatType>(base.getType()))
                base = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, base);
            if (!isa<mlir::FloatType>(rhs.getType()))
                rhs = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, rhs);
        }

        switch (op) {
            case ASTOperator::Less:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::OLT, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::slt, base, rhs);
                break;
            case ASTOperator::LessEqual:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::OLE, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::sle, base, rhs);
                break;
            case ASTOperator::Greater:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::OGT, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::sgt, base, rhs);
                break;
            case ASTOperator::GreaterEqual:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::OGE, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::sge, base, rhs);
                break;
            default:
                llvm_unreachable("no other relational ops");
        }
        base = builder->create<mlir::arith::ExtUIOp>(builder->getUnknownLoc(), i32, base);
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionEquality(ExpressionEquality *exp) {
    auto base = visitExpression(exp->base);
    if (exp->others.empty())
        return base;

    assert(isa<mlir::IntegerType>(base.getType()) || isa<mlir::FloatType>(base.getType()));

    for (const auto &[op, nxt]: exp->others) {
        auto rhs = visitExpression(nxt);
        assert(isa<mlir::IntegerType>(rhs.getType()) || isa<mlir::FloatType>(rhs.getType()));

        bool is_float = isa<mlir::FloatType>(base.getType()) || isa<mlir::FloatType>(rhs.getType());

        if (is_float) {
            if (!isa<mlir::FloatType>(base.getType()))
                base = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, base);
            if (!isa<mlir::FloatType>(rhs.getType()))
                rhs = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), f32, rhs);
        }

        switch (op) {
            case ASTOperator::Equal:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::OEQ, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::eq, base, rhs);
                break;
            case ASTOperator::NotEqual:
                if (is_float)
                    base = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpFPredicate::ONE, base, rhs);
                else
                    base = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(),
                                                                mlir::arith::CmpIPredicate::ne, base, rhs);
                break;
            default:
                llvm_unreachable("no other relational ops");
        }
        base = builder->create<mlir::arith::ExtUIOp>(builder->getUnknownLoc(), i32, base);
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionAnd(ExpressionAnd *exp) {
    assert(!exp->exps.empty());
    auto base = visitExpression(exp->exps[0]);

    if (exp->exps.size() == 1)
        return base;

    assert(isa<mlir::IntegerType>(base.getType()));

    for (auto nxt: exp->exps | std::views::drop(1)) {
        auto rhs = visitExpression(nxt);
        assert(isa<mlir::IntegerType>(rhs.getType()));
        base = builder->create<mlir::arith::AndIOp>(builder->getUnknownLoc(), base, rhs);
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionExclusiveOr(ExpressionExclusiveOr *exp) {
    assert(!exp->exps.empty());
    auto base = visitExpression(exp->exps[0]);

    if (exp->exps.size() == 1)
        return base;

    assert(isa<mlir::IntegerType>(base.getType()));

    for (auto nxt: exp->exps | std::views::drop(1)) {
        auto rhs = visitExpression(nxt);
        assert(isa<mlir::IntegerType>(rhs.getType()));
        base = builder->create<mlir::arith::XOrIOp>(builder->getUnknownLoc(), base, rhs);
    }

    return base;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionInclusiveOr(ExpressionInclusiveOr *exp) {
    assert(!exp->exps.empty());
    auto base = visitExpression(exp->exps[0]);

    if (exp->exps.size() == 1)
        return base;


    assert(isa<mlir::IntegerType>(base.getType()));

    for (auto nxt: exp->exps | std::views::drop(1)) {
        auto rhs = visitExpression(nxt);
        assert(isa<mlir::IntegerType>(rhs.getType()));
        base = builder->create<mlir::arith::OrIOp>(builder->getUnknownLoc(), base, rhs);
    }

    return base;
}

mlir::Value _short_circuit_logical_and(ast_visitor::MLIR_ASTVisitor *visitor, mlir::OpBuilder &builder,
                                       const std::vector<ast::Expression *> &cond_exps, int i,
                                       mlir::Value init) {
    auto cond = visitor->visitExpression(cond_exps[i]);
    assert(
        isa<mlir::IntegerType>(cond.getType()) || isa<mlir::FloatType>(
            cond.getType()));
    if (cond.getType().isInteger(1)) {
    } else if (cond.getType().isInteger(32)) {
        auto zero = builder.create<mlir::arith::ConstantIntOp>(
            builder.getUnknownLoc(), 0, 32);
        cond = builder.create<mlir::arith::CmpIOp>(
            builder.getUnknownLoc(), mlir::arith::CmpIPredicate::ne,
            cond, zero);
    } else if (cond.getType().isF32()) {
        auto zero = builder.create<mlir::arith::ConstantOp>(
            builder.getUnknownLoc(), builder.getFloatAttr(ast_visitor::f32, 0.0));
        cond = builder.create<mlir::arith::CmpFOp>(
            builder.getUnknownLoc(), mlir::arith::CmpFPredicate::ONE,
            cond, zero);
    } else
        llvm_unreachable("logical only supports i1 i32 and f32");

    if (cond_exps.size() - 1 == i)
        return cond;

    auto curr = builder.create<mlir::arith::AndIOp>(builder.getUnknownLoc(), init, cond);
    auto if_op = builder.create<mlir::scf::IfOp>(builder.getUnknownLoc(),
                                                 mlir::TypeRange{builder.getI1Type()}, curr, true);

    // true branch
    builder.setInsertionPointToStart(&if_op.getThenRegion().front());
    auto val = _short_circuit_logical_and(visitor, builder, cond_exps, i + 1, curr);
    builder.setInsertionPointToEnd(&if_op.getThenRegion().back());
    builder.create<mlir::scf::YieldOp>(builder.getUnknownLoc(),
                                       mlir::ValueRange{val});

    // else branch
    builder.setInsertionPointToStart(&if_op.getElseRegion().front());
    builder.create<mlir::scf::YieldOp>(builder.getUnknownLoc(),
                                       mlir::ValueRange{
                                           builder.create<mlir::arith::ConstantIntOp>(builder.getUnknownLoc(), 0, 1)
                                       });

    return if_op.getResult(0);
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionLogicalAnd(ExpressionLogicalAnd *exp) {
    assert(!exp->exps.empty());

    if (exp->exps.size() == 1)
        return visitExpression(exp->exps[0]);

    mlir::Value value = builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(), 1, 1);

    value = _short_circuit_logical_and(this, *builder, exp->exps, 0, value);

    builder->setInsertionPointAfterValue(value);

    return value;
}

mlir::Value _short_circuit_logical_or(ast_visitor::MLIR_ASTVisitor *visitor, mlir::OpBuilder &builder,
                                      const std::vector<ast::Expression *> &cond_exps, int i,
                                      mlir::Value init) {
    auto cond = visitor->visitExpression(cond_exps[i]);
    assert(
        isa<mlir::IntegerType>(cond.getType()) || isa<mlir::FloatType>(
            cond.getType()));
    if (cond.getType().isInteger(1)) {
    } else if (cond.getType().isInteger(32)) {
        auto zero = builder.create<mlir::arith::ConstantIntOp>(
            builder.getUnknownLoc(), 0, 32);
        cond = builder.create<mlir::arith::CmpIOp>(
            builder.getUnknownLoc(), mlir::arith::CmpIPredicate::ne,
            cond, zero);
    } else if (cond.getType().isF32()) {
        auto zero = builder.create<mlir::arith::ConstantOp>(
            builder.getUnknownLoc(), builder.getFloatAttr(ast_visitor::f32, 0.0));
        cond = builder.create<mlir::arith::CmpFOp>(
            builder.getUnknownLoc(), mlir::arith::CmpFPredicate::ONE,
            cond, zero);
    } else
        llvm_unreachable("logical only supports i1 i32 and f32");

    if (cond_exps.size() - 1 == i)
        return cond;

    auto curr = builder.create<mlir::arith::OrIOp>(builder.getUnknownLoc(), init, cond);
    auto if_op = builder.create<mlir::scf::IfOp>(builder.getUnknownLoc(),
                                                 mlir::TypeRange{builder.getI1Type()}, curr, true);

    // true branch
    builder.setInsertionPointToStart(&if_op.getThenRegion().front());
    builder.create<mlir::scf::YieldOp>(builder.getUnknownLoc(),
                                       mlir::ValueRange{
                                           builder.create<mlir::arith::ConstantIntOp>(builder.getUnknownLoc(), 1, 1)
                                       });

    // else branch
    builder.setInsertionPointToStart(&if_op.getElseRegion().front());
    auto val = _short_circuit_logical_or(visitor, builder, cond_exps, i + 1, curr);
    builder.setInsertionPointToEnd(&if_op.getElseRegion().back());
    builder.create<mlir::scf::YieldOp>(builder.getUnknownLoc(), mlir::ValueRange{val});

    return if_op.getResult(0);
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionLogicalOr(ExpressionLogicalOr *exp) {
    assert(!exp->exps.empty());

    if (exp->exps.size() == 1)
        return visitExpression(exp->exps[0]);

    mlir::Value value = builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(), 0, 1);

    value = _short_circuit_logical_or(this, *builder, exp->exps, 0, value);

    builder->setInsertionPointAfterValue(value);

    return value;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionConditional(ExpressionConditional *exp) {
    // assert((!exp->former && !exp->latter) || (exp->former && exp->latter));
    // auto cond = visitExpression(exp->cond);

    // todo: implement this

    return visitExpression(exp->cond);
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionAssignment(ExpressionAssignment *exp) {
    if (exp->assignment) {
        const auto [op, rhs] = exp->assignment.value();
        auto base = dyn_cast<ExpressionUnary>(exp->base);
        assert(base);
        assert(op == ASTOperator::Assign);
        auto value = visitExpression(rhs);
        // 标量赋值
        if (auto pri = dyn_cast<ExpressionPrimary>(base->exp); pri) {
            assert(std::holds_alternative<std::string>(pri->value));
            const auto &name = std::get<std::string>(pri->value);
            auto [address, type, global_attr] = scope.get(name);

            auto ele_type = type.dyn_cast<mlir::MemRefType>().getElementType();
            auto val_type = value.getType();

            if (ele_type != val_type) {
                if (ele_type.isInteger(32) && val_type.isF32()) {
                    value = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), ele_type, value);
                } else if (ele_type.isF32() && val_type.isInteger(32)) {
                    value = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), ele_type, value);
                } else
                    llvm_unreachable("no other init list type conversions.");
            }

            if (!address) {
                // global
                assert(!global_attr);
                auto get_global = builder->create<mlir::memref::GetGlobalOp>(builder->getUnknownLoc(), type, name);
                builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), value, get_global, mlir::ValueRange{});
            } else {
                builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), value, address, mlir::ValueRange{});
            }

            return nullptr;
        } else if (auto postfix = dyn_cast<ExpressionPostfix>(base->exp); postfix) {
            assert(postfix->op == ASTOperator::Subscript);
            auto array_base = postfix->primary;
            assert(array_base && std::holds_alternative<string>(array_base->value));
            const auto &name = std::get<string>(array_base->value);

            auto [address, type, global_attr] = scope.get(name);
            if (!address) {
                address = builder->create<mlir::memref::GetGlobalOp>(builder->getUnknownLoc(), type, name);
            } else if (dyn_cast<mlir::MemRefType>(type).getElementType().isa<mlir::MemRefType>()) {
                address = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), address);
            }

            auto memref_type = dyn_cast<mlir::MemRefType>(address.getType());
            auto shape = memref_type.getShape();
            assert(shape.size() == postfix->exps.size());

            auto indices = postfix->exps | std::views::transform([this](Expression *exp) {
                auto val = visitExpression(exp);
                assert(val.getType().isInteger());
                return builder->create<
                    mlir::arith::IndexCastOp>(builder->getUnknownLoc(), builder->getIndexType(), val);
            }) | std::ranges::to<vector<mlir::Value> >();

            auto ele_type = memref_type.getElementType();
            auto val_type = value.getType();

            if (ele_type != val_type) {
                if (ele_type.isInteger(32) && val_type.isF32()) {
                    value = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), ele_type, value);
                } else if (ele_type.isF32() && val_type.isInteger(32)) {
                    value = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), ele_type, value);
                } else
                    llvm_unreachable("no other init list type conversions.");
            }

            builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), value, address, indices);

            return nullptr;
        } else
            llvm_unreachable("no other assignment patterns");
    }

    return visitExpression(exp->base);
}

// todo:
//  1. local数组添加memset
//  2. 修正global数组初始化逻辑
size_t ast_visitor::MLIR_ASTVisitor::_generate_init_list_global(const vector<Expression *> &exps,
                                                                size_t current_align,
                                                                size_t current_index,
                                                                llvm::ArrayRef<long> dims,
                                                                vector<int> &values) {
    for (auto item: exps) {
        if (auto init_list = dyn_cast<ExpressionInitializerList>(item)) {
            size_t next_align = 1;
            size_t tmp_index = dims.size() - 1;
            while (current_index % next_align == 0 && tmp_index > 0) {
                next_align *= dims[tmp_index];
                tmp_index--;
            }

            current_index = _generate_init_list_global(init_list->exps, next_align, current_index, dims, values);
        } else {
            auto value = visitExpression(item);
            assert(isa<mlir::arith::ConstantIntOp>(value.getDefiningOp()));
            values[current_index] = extractFromConstantValue<int>(value);
            current_index++;
        }
    }
    return current_index % current_align == 0
               ? current_index
               : (current_index / current_align + 1) * current_align;
}

// https://blog.csdn.net/weixin_56462041/article/details/128908399
mlir::DenseElementsAttr ast_visitor::MLIR_ASTVisitor::visitExpressionInitializerListGlobal(
    ExpressionInitializerList *exp,
    llvm::ArrayRef<long> dims,
    mlir::Type type
) {
    auto n = std::ranges::fold_left(dims, 1, std::multiplies{});
    auto current_align = n / dims[0];


    if (type.isInteger()) {
        vector<int> res(n, 0);
        _generate_init_list_global(exp->exps, current_align, 0, dims, res);
        auto data_type = mlir::RankedTensorType::get(dims, i32);
        auto init_value = mlir::DenseElementsAttr::get(data_type, llvm::ArrayRef(res));
        return init_value;
    } else {
        llvm_unreachable("unimplemented: float initializer list");
    }
}


size_t ast_visitor::MLIR_ASTVisitor::_generate_init_list_local(const vector<Expression *> &exps, size_t current_align,
                                                               size_t current_index, size_t current_nested,
                                                               llvm::ArrayRef<long> dims, const mlir::Value alloca_op) {
    for (auto item: exps) {
        if (auto init_list = dyn_cast<ExpressionInitializerList>(item)) {
            size_t next_align = 1;
            size_t tmp_index = dims.size() - 1;
            while (current_index % next_align == 0 && tmp_index > 0) {
                next_align *= dims[tmp_index];
                tmp_index--;
            }

            current_index = _generate_init_list_local(init_list->exps, next_align, current_index, current_nested + 1,
                                                      dims, alloca_op);
        } else {
            auto value = visitExpression(item);
            // 计算对应的下标
            llvm::SmallVector<size_t, 8> sv(dims.size());
            size_t tmp_index = dims.size() - 1;
            std::ranges::fold_right(dims, current_index, [&sv, &tmp_index](size_t dim, size_t acc) {
                sv[tmp_index] = acc % dim;
                tmp_index--;
                return acc / dim;
            });
            auto indices = sv
                           | std::views::transform([this](size_t index) {
                               return builder->create<mlir::arith::ConstantIndexOp>(builder->getUnknownLoc(), index);
                           })
                           | std::ranges::to<vector<mlir::Value> >();
            auto vr = mlir::ValueRange(indices);

            auto ele_type = alloca_op.getType().dyn_cast<mlir::MemRefType>().getElementType();
            auto val_type = value.getType();

            if (ele_type != val_type) {
                if (ele_type.isInteger(32) && val_type.isF32()) {
                    value = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), ele_type, value);
                } else if (ele_type.isF32() && val_type.isInteger(32)) {
                    value = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), ele_type, value);
                } else
                    llvm_unreachable("no other init list type conversions.");
            }

            builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), value, alloca_op, vr);

            current_index++;
        }
    }
    auto return_index = current_index % current_align == 0
                            ? current_index
                            : (current_index / current_align + 1) * current_align;
    if (exps.empty()) {
        return_index += std::ranges::fold_left(dims | std::views::drop(current_nested), 1, std::multiplies{});
    }
    return return_index;
}

void ast_visitor::MLIR_ASTVisitor::visitExpressionInitializerListLocal(
    ExpressionInitializerList *exp,
    llvm::ArrayRef<long> dims,
    mlir::Type type, const mlir::Value alloca_op
) {
    auto n = std::ranges::fold_left(dims, 1, std::multiplies{});
    auto current_align = n / dims[0];

    _generate_init_list_local(exp->exps, current_align, 0, 0, dims, alloca_op);
}

// 前端处理的时候, 没有后缀的视为primary expression
// 所以无须处理
mlir::Value ast_visitor::MLIR_ASTVisitor::visitExpressionPostfix(ExpressionPostfix *exp) {
    assert(std::holds_alternative<string>(exp->primary->value));
    const auto &name = std::get<string>(exp->primary->value);

    switch (exp->op) {
        case ASTOperator::FunctionCall: {
            auto args = exp->exps | std::views::transform([this](Expression *exp) { return visitExpression(exp); }) |
                        std::ranges::to<vector<mlir::Value> >();

            if (name == "putarray" || name == "putfarray") {
                // 第二个参数类型是llvm.ptr
                auto raw = args[1];
                assert(isa<mlir::MemRefType>(raw.getType()));
                auto extract = builder->create<mlir::memref::ExtractStridedMetadataOp>(builder->getUnknownLoc(), raw);
                auto offset_as_index = extract.getResult(1);
                auto base_buffer_as_index = builder->create<mlir::memref::ExtractAlignedPointerAsIndexOp>(
                    builder->getUnknownLoc(), raw);
                auto base_buffer_as_i64 = builder->create<mlir::arith::IndexCastOp>(
                    builder->getUnknownLoc(), builder->getI64Type(), base_buffer_as_index);
                auto offset_as_i64 = builder->create<mlir::arith::IndexCastOp>(
                    builder->getUnknownLoc(), builder->getI64Type(), offset_as_index);
                auto element_size_i64 = builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(), 4, 64);
                auto byte_offset_as_i64 = builder->create<mlir::arith::MulIOp>(
                    builder->getUnknownLoc(), offset_as_i64, element_size_i64);
                auto address_as_i64 = builder->create<mlir::arith::AddIOp>(
                    builder->getUnknownLoc(), base_buffer_as_i64, byte_offset_as_i64);
                auto base_buffer_as_ptr = builder->create<mlir::LLVM::IntToPtrOp>(
                    builder->getUnknownLoc(), ptr, address_as_i64);
                args[1] = base_buffer_as_ptr;
            }

            if (name == "getarray" || name == "getfarray") {
                // 第一个参数是llvm.ptr
                auto raw = args[0];
                assert(isa<mlir::MemRefType>(raw.getType()));
                auto extract = builder->create<mlir::memref::ExtractStridedMetadataOp>(builder->getUnknownLoc(), raw);
                auto offset_as_index = extract.getResult(1);
                auto base_buffer_as_index = builder->create<mlir::memref::ExtractAlignedPointerAsIndexOp>(
                    builder->getUnknownLoc(), raw);
                auto base_buffer_as_i64 = builder->create<mlir::arith::IndexCastOp>(
                    builder->getUnknownLoc(), builder->getI64Type(), base_buffer_as_index);
                auto offset_as_i64 = builder->create<mlir::arith::IndexCastOp>(
                    builder->getUnknownLoc(), builder->getI64Type(), offset_as_index);
                auto element_size_i64 = builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(), 4, 64);
                auto byte_offset_as_i64 = builder->create<mlir::arith::MulIOp>(
                    builder->getUnknownLoc(), offset_as_i64, element_size_i64);
                auto address_as_i64 = builder->create<mlir::arith::AddIOp>(
                    builder->getUnknownLoc(), base_buffer_as_i64, byte_offset_as_i64);
                auto base_buffer_as_ptr = builder->create<mlir::LLVM::IntToPtrOp>(
                    builder->getUnknownLoc(), ptr, address_as_i64);
                args[0] = base_buffer_as_ptr;
            }

            const auto [callee, callee_type] = scope.getFunc(name);
            for (auto i = 0; i < args.size(); i++) {
                auto expected_type = callee_type.getInput(i);
                auto current_type = args[i].getType();

                if (expected_type == current_type)
                    continue;

                if (auto expected_memref_type = dyn_cast<mlir::MemRefType>(expected_type),
                            current_memref_type = dyn_cast<mlir::MemRefType>(current_type);
                    expected_memref_type && current_memref_type) {
                    assert(expected_memref_type.getShape().size() == current_memref_type.getShape().size());
                    assert(
                        expected_memref_type.getShape()[0] == mlir::ShapedType::kDynamic && current_memref_type.getShape
                        ()[0] != mlir::ShapedType::kDynamic);

                    auto cast_op = builder->create<mlir::memref::CastOp>(
                        builder->getUnknownLoc(), expected_type, args[i]);
                    args[i] = cast_op;

                    continue;
                }
                if (expected_type.isF32() && current_type.isInteger(32)) {
                    auto casted = builder->create<mlir::arith::SIToFPOp>(
                        builder->getUnknownLoc(), expected_type, args[i]);
                    args[i] = casted;

                    continue;
                }

                if (expected_type.isInteger(32) && current_type.isF32()) {
                    auto casted = builder->create<mlir::arith::FPToSIOp>(
                        builder->getUnknownLoc(), expected_type, args[i]);
                    args[i] = casted;

                    continue;
                }

                llvm_unreachable("function arguments don't match");
            }
            auto call = builder->create<mlir::func::CallOp>(builder->getUnknownLoc(), callee, args);

            if (call.getNumResults() == 0)
                return nullptr;

            assert(call.getNumResults() == 1);
            return call.getResult(0);

            llvm_unreachable("unimplemented: function call");
        }
        break;
        case ASTOperator::PostPlusPlus:
            llvm_unreachable("unimplemented: post-plus-plus");
        case ASTOperator::PostMinusMinus:
            llvm_unreachable("unimplemented: post-minus-minus");
        case ASTOperator::Subscript: {
            // assert(_subscript_info);
            // todo: 默认右值
            mlir::Value res = nullptr;
            auto indices = exp->exps | std::views::transform([this](auto e) { return visitExpression(e); })
                           | std::views::transform([this](mlir::Value v) {
                               return static_cast<mlir::Value>(builder->create<mlir::arith::IndexCastOp>(
                                   builder->getUnknownLoc(), builder->getIndexType(), v));
                           })
                           | std::ranges::to<vector>();
            auto vr = mlir::ValueRange(indices);
            auto [value, type, _] = scope.get(name);

            mlir::Value ptr;
            if (value)
                if (dyn_cast<mlir::MemRefType>(type).getElementType().isa<mlir::MemRefType>())
                    // 例如: memref<memref<?x3xi32>>
                    // int[][3]出现在函数参数列表里时，需要alloca一块空间存放
                    // 这块空间的类型就是嵌套的memref
                    ptr = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), value);
                else
                    ptr = value;
            else {
                // global
                ptr = builder->create<mlir::memref::GetGlobalOp>(builder->getUnknownLoc(), type, name);
            }

            auto source_mem_ref_type = dyn_cast<mlir::MemRefType>(ptr.getType());
            if (indices.size() == source_mem_ref_type.getRank())
                res = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), ptr, vr);
            else if (indices.size() < source_mem_ref_type.getRank()) {
                // int a[10][20];
                // func(a[0]);
                // assert(indices.size() + 1 == dyn_cast<mlir::MemRefType>(ptr.getType()).getRank());

                auto raw_shape = source_mem_ref_type.getShape();

                auto subview_offsets = std::views::iota(0ull, raw_shape.size()) | std::views::transform([&](size_t i) {
                    if (i < indices.size()) {
                        auto maybe_index = indices[i];
                        assert(maybe_index.getType().isIndex() || maybe_index.getType().isInteger());
                        if (!maybe_index.getType().isIndex()) {
                            maybe_index = builder->create<mlir::arith::IndexCastOp>(
                                builder->getUnknownLoc(), builder->getIndexType(), maybe_index);
                        }
                        return mlir::OpFoldResult(maybe_index);
                    }
                    return mlir::OpFoldResult(builder->getIndexAttr(0));
                }) | std::ranges::to<vector>();

                auto subview_sizes = std::views::iota(0ull, raw_shape.size()) | std::views::transform([&](size_t i) {
                    if (i < indices.size()) {
                        return mlir::OpFoldResult(builder->getIndexAttr(1));
                    }
                    return mlir::OpFoldResult(builder->getIndexAttr(raw_shape[i]));
                }) | std::ranges::to<vector>();

                auto subview_strides = vector<mlir::OpFoldResult>(raw_shape.size(), builder->getIndexAttr(1));

                auto subview_res_shape = llvm::ArrayRef(raw_shape.begin() + indices.size(), raw_shape.end());

                auto subview_result_type = mlir::memref::SubViewOp::inferRankReducedResultType(
                    subview_res_shape, source_mem_ref_type, subview_offsets, subview_sizes,
                    subview_strides).dyn_cast<mlir::MemRefType>();

                auto subview = builder->create<mlir::memref::SubViewOp>(builder->getUnknownLoc(), subview_result_type,
                                                                        ptr, subview_offsets, subview_sizes,
                                                                        subview_strides);

                auto normal_memref_type =
                        mlir::MemRefType::get(subview_res_shape, source_mem_ref_type.getElementType());

                // res = builder->create<mlir::memref::CastOp>(builder->getUnknownLoc(), normal_memref_type, subview);
                res = subview;
            } else
                llvm_unreachable("too many indices");

            return res;
        }
        break;
        default:
            llvm_unreachable("no other postfix op.");
    }

    llvm_unreachable("unimplemented: visitExpressionPostfix");
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitASTBase(ASTBase *base) {
    if (auto exp = dyn_cast<Expression>(base); exp)
        return visitExpression(exp);
    if (auto statement = dyn_cast<Statement>(base); statement)
        return visitStatement(statement);
    if (auto decl = dyn_cast<Declaration>(base); decl)
        return visitDeclaration(decl);

    llvm_unreachable("no other types.");
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementExpression(StatementExpression *statement) {
    if (statement->expression == nullptr)
        return nullptr;
    return visitExpression(statement->expression);
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementReturn(StatementReturn *ret) {
    assert(curr_func && curr_func_ret_block);
    if (ret->return_value != nullptr) {
        auto ret_value_raw = visitExpression(ret->return_value);
        mlir::Value ret_value;

        const auto ret_op = ret_value_raw.getDefiningOp();
        if (dyn_cast<mlir::memref::AllocaOp>(ret_op))
            ret_value = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), ret_value_raw,
                                                              mlir::ValueRange{});

        else if (auto get_global = dyn_cast<mlir::memref::GetGlobalOp>(ret_op))
            ret_value = builder->create<mlir::memref::LoadOp>(builder->getUnknownLoc(), get_global, mlir::ValueRange{});

        else {
            ret_value = ret_value_raw;
        }

        auto return_type = curr_func_ret_value.getType().dyn_cast<mlir::MemRefType>().getElementType();
        auto current_type = ret_value.getType();

        if (return_type != current_type) {
            if (return_type.isInteger(32) && current_type.isF32()) {
                ret_value = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), return_type, ret_value);
            } else if (return_type.isF32() && current_type.isInteger(32)) {
                ret_value = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), return_type, ret_value);
            } else
                llvm_unreachable("no other return type conversions.");
        }

        builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), ret_value, curr_func_ret_value);
    }
    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), curr_func_ret_block);
    return nullptr;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementCompound(StatementCompound *compound) {
    scope.enterScope();
    for (auto item: compound->children)
        visitASTBase(item);
    scope.leaveScope();
    return nullptr;
}

// todo: implement this in scf instead of cf dialect
mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementIfElse(StatementIfElse *selection) {
    auto cond = visitExpression(selection->condition);
    assert((isa<mlir::IntegerType, mlir::FloatType>(cond.getType())));
    if (cond.getType().isInteger(32)) {
        cond = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(), mlir::arith::CmpIPredicate::ne, cond,
                                                    builder->create<mlir::arith::ConstantIntOp>(
                                                        builder->getUnknownLoc(), 0, 32));
    } else if (cond.getType().isF32()) {
        cond = builder->create<mlir::arith::CmpFOp>(builder->getUnknownLoc(), mlir::arith::CmpFPredicate::ONE, cond,
                                                    getConstant(0.0f));
    } else if (cond.getType().isInteger(1)) {
        // do nothing.
    } else
        llvm_unreachable("no other type for condition");
    mlir::Block *curr_block = nullptr;
    if (selection->else_content) {
        auto if_then_block = curr_func.addBlock();
        auto if_else_block = curr_func.addBlock();
        auto if_exit_block = curr_func.addBlock();

        builder->create<mlir::cf::CondBranchOp>(builder->getUnknownLoc(), cond, if_then_block, if_else_block);


        builder->setInsertionPointToStart(if_then_block);
        visitStatement(selection->then_content);
        curr_block = builder->getBlock();
        if (curr_block->empty() || !curr_block->back().hasTrait<mlir::OpTrait::IsTerminator>())
            builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), if_exit_block);

        builder->setInsertionPointToStart(if_else_block);
        visitStatement(selection->else_content);
        curr_block = builder->getBlock();
        if (curr_block->empty() || !curr_block->back().hasTrait<mlir::OpTrait::IsTerminator>())
            builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), if_exit_block);

        builder->setInsertionPointToStart(if_exit_block);
    } else {
        auto if_then_block = curr_func.addBlock();
        auto if_exit_block = curr_func.addBlock();
        builder->create<mlir::cf::CondBranchOp>(builder->getUnknownLoc(), cond, if_then_block, if_exit_block);

        builder->setInsertionPointToStart(if_then_block);
        visitStatement(selection->then_content);
        curr_block = builder->getBlock();
        // 只有不是终结语句才跳转到默认的if exit block
        if (curr_block->empty() || !curr_block->back().hasTrait<mlir::OpTrait::IsTerminator>())
            builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), if_exit_block);

        builder->setInsertionPointToStart(if_exit_block);
    }
    return nullptr;
}

// todo: implement this in scf instead of cf dialect
mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementWhile(StatementWhile *_while) {
    assert(curr_func);
    auto while_start_block = curr_func.addBlock();
    auto while_body_block = curr_func.addBlock();
    auto while_exit_block = curr_func.addBlock();

    while_info.emplace_back(while_start_block, while_body_block, while_exit_block);


    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), while_start_block);
    builder->setInsertionPointToStart(while_start_block);
    auto cond = visitExpression(_while->condition);
    assert(isa<mlir::IntegerType>(cond.getType()));
    if (cond.getType().isInteger(32)) {
        cond = builder->create<mlir::arith::CmpIOp>(builder->getUnknownLoc(), mlir::arith::CmpIPredicate::ne, cond,
                                                    builder->create<mlir::arith::ConstantIntOp>(
                                                        builder->getUnknownLoc(), 0, 32));
    }

    builder->create<mlir::cf::CondBranchOp>(builder->getUnknownLoc(), cond, while_body_block, while_exit_block);

    builder->setInsertionPointToStart(while_body_block);
    visitStatement(_while->while_content);

    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), while_start_block);

    builder->setInsertionPointToEnd(while_exit_block);

    while_info.pop_back();

    return nullptr;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementBreak(StatementBreak *_break) {
    assert(!while_info.empty());

    auto [start, body, exit] = while_info.back();
    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), exit);

    return nullptr;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatementContinue(StatementContinue *_continue) {
    assert(!while_info.empty());

    auto [start, body, exit] = while_info.back();
    builder->create<mlir::cf::BranchOp>(builder->getUnknownLoc(), start);

    return nullptr;
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitStatement(Statement *statement) {
    if (auto exp = dyn_cast<StatementExpression>(statement); exp)
        return visitStatementExpression(exp);
    if (auto ret = dyn_cast<StatementReturn>(statement); ret)
        return visitStatementReturn(ret);
    if (auto compound = dyn_cast<StatementCompound>(statement))
        return visitStatementCompound(compound);
    if (auto selection = dyn_cast<StatementIfElse>(statement); selection)
        return visitStatementIfElse(selection);
    if (auto _while = dyn_cast<StatementWhile>(statement); _while)
        return visitStatementWhile(_while);
    if (auto _break = dyn_cast<StatementBreak>(statement); _break)
        return visitStatementBreak(_break);
    if (auto _continue = dyn_cast<StatementContinue>(statement); _continue)
        return visitStatementContinue(_continue);
    llvm_unreachable("not implemented");
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitDeclaration(Declaration *decl) {
    if (auto func = dyn_cast<DeclarationFunction>(decl); func)
        return visitDeclarationFunction(func);

    if (auto builtin = dyn_cast<DeclarationBuiltinType>(decl); builtin)
        return visitDeclarationBuiltinType(builtin);

    llvm_unreachable("not implemented");
}

mlir::Value ast_visitor::MLIR_ASTVisitor::visitDeclarationBuiltinType(DeclarationBuiltinType *decl) {
    mlir::Type type = ASTType2MLIRType(decl->type);
    const auto &name = decl->name;
    // assert(decl->dimensions.empty());
    vector<mlir::Value> _dims;
    for (auto exp: decl->dimensions)
        _dims.push_back(exp ? visitExpression(exp) : nullptr);

    assert(
        std::ranges::all_of(_dims, [this](mlir::Value v) {return (curr_func && !v) || isa<mlir::arith::ConstantIntOp>(v.
            getDefiningOp());}));

    vector<long> dims;
    for (auto v: _dims)
        dims.push_back(v ? extractFromConstantValue<int>(v) : mlir::ShapedType::kDynamic);


    mlir::Value value;
    mlir::Type value_type;
    mlir::TypedAttr global_attr = nullptr;

    if (scope.isGlobal()) {
        assert(dims.empty() || dims[0] != mlir::ShapedType::kDynamic);

        auto global_type = mlir::MemRefType::get(dims, type);

        bool has_init = decl->initializer != nullptr;
        mlir::DenseElementsAttr attr;

        if (has_init) {
            if (auto init_list = dyn_cast<ExpressionInitializerList>(decl->initializer))
                attr = visitExpressionInitializerListGlobal(init_list, dims, type);
            else {
                assert(dims.empty());
                auto init_val = visitExpression(decl->initializer);
                mlir::TypedAttr const_scalar_global_attr;
                const_scalar_global_attr = dyn_cast<mlir::arith::ConstantOp>(init_val.getDefiningOp()).getValueAttr();

                if (const_scalar_global_attr.getType() != type) {
                    if (const_scalar_global_attr.getType().isInteger() && type.isF32()) {
                        const_scalar_global_attr = builder->getFloatAttr(
                            type, dyn_cast<mlir::IntegerAttr>(const_scalar_global_attr).getInt());
                    } else if (const_scalar_global_attr.getType().isF32() && const_scalar_global_attr.getType().
                               isF32()) {
                        const_scalar_global_attr = builder->getIntegerAttr(
                            type, static_cast<int64_t>(dyn_cast<mlir::FloatAttr>(const_scalar_global_attr).
                                getValueAsDouble()));
                    } else
                        llvm_unreachable("unknown other type conversions");
                }

                attr = mlir::DenseElementsAttr::get(
                    mlir::RankedTensorType::get(dims, type),
                    const_scalar_global_attr
                );
                if (decl->isConstDecl())
                    global_attr = const_scalar_global_attr;
            }
        }

        builder->create<mlir::memref::GlobalOp>(builder->getUnknownLoc(),
                                                builder->getStringAttr(name),
                                                builder->getStringAttr("private"),
                                                global_type,
                                                has_init ? attr : nullptr,
                                                decl->isConstDecl(),
                                                nullptr);
        value_type = global_type;
        value = nullptr;
    } else if (decl->isConstDecl() && dims.empty()) {
        // const scalar
        assert(decl->initializer != nullptr);
        value = visitExpression(decl->initializer);
        assert(isa<mlir::arith::ConstantOp>(value.getDefiningOp()));
        value_type = type;
    } else {
        mlir::MemRefType memRefType;
        // non-const scalar or vector
        if (!dims.empty() && dims[0] == mlir::ShapedType::kDynamic) {
            // function argument like int a[][10]
            // it's just a pointer
            assert(curr_func);
            // assert(dims.size() == 1);
            if (dims.size() == 1) {
                vector<int64_t> strides(dims.size(), 1);
                auto layout = mlir::StridedLayoutAttr::get(ctx, mlir::ShapedType::kDynamic, strides);
                memRefType = mlir::MemRefType::get({}, mlir::MemRefType::get(dims, type, layout));
            } else {
                memRefType = mlir::MemRefType::get({}, mlir::MemRefType::get(dims, type));
            }
        } else
            memRefType = mlir::MemRefType::get(dims, type);

        value = builder->create<mlir::memref::AllocaOp>(builder->getUnknownLoc(),
                                                        memRefType);

        if (isa<mlir::IntegerType, mlir::FloatType>(memRefType.getElementType())) {
            mlir::Value zero_value = memRefType.getElementType().isInteger()
                                         ? builder->create<mlir::arith::ConstantIntOp>(builder->getUnknownLoc(), 0, 32).
                                         getResult()
                                         : builder->create<mlir::arith::ConstantFloatOp>(
                                             builder->getUnknownLoc(), llvm::APFloat(0.0f),
                                             type.dyn_cast<mlir::FloatType>()).getResult();
            builder->create<mlir::linalg::FillOp>(builder->getUnknownLoc(), zero_value, value);
        }

        if (decl->initializer != nullptr) {
            assert(dims.empty() || dims[0] != mlir::ShapedType::kDynamic);
            if (auto init_list = dyn_cast<ExpressionInitializerList>(decl->initializer))
                visitExpressionInitializerListLocal(init_list, dims, type, value);
            else {
                auto init = visitExpression(decl->initializer);

                auto ele_type = memRefType.getElementType();
                auto val_type = init.getType();

                if (ele_type != val_type) {
                    if (ele_type.isInteger(32) && val_type.isF32()) {
                        init = builder->create<mlir::arith::FPToSIOp>(builder->getUnknownLoc(), ele_type, init);
                    } else if (ele_type.isF32() && val_type.isInteger(32)) {
                        init = builder->create<mlir::arith::SIToFPOp>(builder->getUnknownLoc(), ele_type, init);
                    } else
                        llvm_unreachable("no other init list type conversions.");
                }

                builder->create<mlir::memref::StoreOp>(builder->getUnknownLoc(), init, value);
            }
        }
        value_type = memRefType;
    }
    scope.set(name, {value, value_type, global_attr});


    return value;
}
